#include "co.h"
// #include "am.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <setjmp.h>
#include <assert.h>
#include <time.h>
#define LOCAL_MACHINE
#ifdef LOCAL_MACHINE
FILE *fp = NULL;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif

/// @brief 将当前rsp保存 然后调用函数 然后回到rsp
/// @param sp
/// @param entry
/// @param arg
static inline void
stack_switch_call(void *sp, void *entry, uintptr_t arg)
{
    asm volatile(
#if __x86_64__
        "movq %0, %%rsp; movq %2, %%rdi; jmp *%1"
        :
        : "b"((uintptr_t)sp),
          "d"(entry),
          "a"(arg)
        : "memory"
#else
        "movl %0, %%esp; movl %2, 4(%0); jmp *%1"
        :
        : "b"((uintptr_t)sp - 8),
          "d"(entry),
          "a"(arg)
        : "memory"
#endif
    );
}

typedef enum co_status
{
    CO_NEW = 1, // 新创建，还未执行过
    CO_RUNNING, // 已经执行过
    CO_WAITING, // 在 co_wait 上等待
    CO_DEAD,    // 已经结束，但还未释放资源
} co_status;

#define STACK_SIZE (8 * 4096 + 1)

struct co
{
    char *name;
    void (*func)(void *);
    void *arg;

    co_status status;
    struct co *waiter;
    jmp_buf context;
    uint8_t stack[STACK_SIZE];
};

typedef struct co Co;
Co *currCo = NULL;

Co *newCo(const char *, void (*)(void *), void *);
void deleteCo(Co *);

typedef Co Element;
void (*deleteElement)(Element *) = deleteCo;

typedef struct MyListNode
{
    Element *pElement;
    struct MyListNode *next;
} MyListNode;
MyListNode *newListNode(Element *);
void deleteListNode(MyListNode *, int);

typedef struct MyList
{
    int length;
    MyListNode root;
} MyList;
MyList *newList();
Element *myListRandomGet(MyList *);
Element *myListGet(MyList *, int);
int myFilter(Element *);
MyList *myListFilter(MyList *, int (*)(Element *));
void myListPushFront(MyList *, Element *);
void myListErase(MyList *, Element *);
void clearList(MyList *, int);
void deleteList(MyList *, int);

MyList *coList = NULL;

__attribute__((constructor)) void co_init()
{
    srand(time(NULL));
    coList = newList();
    currCo = newCo("main", NULL, NULL);
    currCo->status = CO_RUNNING;
    myListPushFront(coList, currCo);
#ifdef LOCAL_MACHINE
    fp = fopen("stderr.txt", "w");
#endif
}

struct co *co_start(const char *name, void (*func)(void *), void *arg)
{
    Co *newco = newCo(name, func, arg);
    myListPushFront(coList, newco);
    return newco;
}

void co_wait(struct co *co)
{
    assert(currCo);
    debug("Enter Co:%s\n", co->name);
    if (co->status == CO_DEAD)
    {
        debug("Co %s has dead\n", co->name);
    }
    else
    {
        currCo->status = CO_WAITING;
        co->waiter = currCo;
        co_yield ();
    }
    debug("Leave Co:%s\n", co->name);
    myListErase(coList, co);
    deleteCo(co);
}

void switchNewCo()
{
    currCo->status = CO_RUNNING;
    stack_switch_call(&currCo->stack[STACK_SIZE - 1], currCo->func, (uintptr_t)currCo->arg);
    currCo->status = CO_DEAD;
    if (currCo->waiter)
    {
        currCo->waiter = NULL;
        currCo->waiter->status = CO_RUNNING;
    }
}

void co_yield ()
{
    assert(currCo != NULL);
    int val = setjmp(currCo->context);
    if (val == 0)
    {
        // 更换currCo
        MyList *tmp = myListFilter(coList, myFilter);
        Co *nextCo = myListRandomGet(tmp);
        deleteList(tmp, 0);
        assert(nextCo);
        // debug("from %s change to %s\n", currCo->name, nextCo->name);
        currCo = nextCo;
        if (currCo->status == CO_NEW)
        { // 新的！
          // debug("New Co\n");
            switchNewCo();
        }
        else if (currCo->status == CO_RUNNING)
        {
            longjmp(currCo->context, 1);
        }
    }
    else
    {
    }
}

Co *newCo(const char *name, void (*func)(void *), void *arg)
{
    Co *tmp = (Co *)malloc(sizeof(Co));
    tmp->name = (char *)malloc(sizeof(char) * (1 + strlen(name)));
    strcpy(tmp->name, name);
    tmp->func = func;
    tmp->arg = arg;
    tmp->status = CO_NEW;
    memset(tmp->stack, 0, sizeof(tmp->stack));
    return tmp;
}

void deleteCo(Co *co)
{
    if (co->name)
        free(co->name);
    free(co);
}

MyListNode *newListNode(Element *e)
{
    MyListNode *tmp = (MyListNode *)malloc(sizeof(MyListNode));
    tmp->next = NULL;
    tmp->pElement = e;
    return tmp;
}

void deleteListNode(MyListNode *pnode, int model)
{
    if (!pnode)
    {
        return;
    }
    if (model == 1)
    {
        deleteElement(pnode->pElement);
    }
    free(pnode);
}

MyList *newList()
{
    MyList *tmp = (MyList *)malloc(sizeof(MyList));
    tmp->length = 0;
    tmp->root.next = NULL;
    tmp->root.pElement = NULL;
    return tmp;
}

// 获取 第 id 个 元素 从0开始
Element *myListGet(MyList *list, int id)
{
    if (id < 0 || list->length == 0)
    {
        return NULL;
    }
    id = id % list->length;
    for (MyListNode *pnode = list->root.next; pnode != NULL; pnode = pnode->next)
    {
        if (id == 0)
        {
            return pnode->pElement;
        }
        --id;
    }
    return NULL;
}

Element *myListRandomGet(MyList *list)
{
    if (list->length > 0)
    {
        return myListGet(list, rand() % list->length);
    }
    return NULL;
}

int myFilter(Element *e)
{
    return e->status == CO_NEW || e->status == CO_RUNNING;
}

// 获取 满足filter == 1的元素
MyList *myListFilter(MyList *list, int (*filter)(Element *))
{
    MyList *tmp = newList();
    MyListNode *pnode = list->root.next;
    while (pnode)
    {
        if (filter(pnode->pElement) == 1)
        {
            myListPushFront(tmp, pnode->pElement);
        }
        pnode = pnode->next;
    }
    return tmp;
}

void myListPushFront(MyList *list, Element *e)
{
    if (!e)
    {
        return;
    }
    MyListNode *newNode = newListNode(e);
    newNode->pElement = e;
    newNode->next = list->root.next;
    list->root.next = newNode;
    ++list->length;
}

void myListErase(MyList *list, Element *e)
{
    MyListNode *pnode = &list->root;
    while (pnode->next)
    {
        MyListNode *tmp = pnode->next;
        if (tmp->pElement != e)
        {
            pnode = pnode->next;
            continue;
        }
        --list->length;
        pnode->next = tmp->next;
        deleteListNode(tmp, 0);
        break;
    }
}

void clearList(MyList *list, int model)
{
    MyListNode *pnode = list->root.next;
    while (pnode)
    {
        MyListNode *tmp = pnode;
        pnode = pnode->next;
        deleteListNode(tmp, model);
    }
    list->length = 0;
    list->root.next = NULL;
}

// 1：删除元素 0：不删除
void deleteList(MyList *list, int model)
{
    clearList(list, model);
    free(list);
}